iT邦幫忙

2026 iThome 鐵人賽

DAY 21
0
Software Development

我的 SDD 實驗之路 - 從實際使用現有工具,到設計自己的流程系列 第 21 篇

【Day - 21】多份 changes 一起做,Speclink 怎麼安排順序與工作目錄?

  • 分享至 

  • xImage
  •  

【Day - 20】把 task 完成時的檔案紀錄留下來了。不過,如果好幾份 changes 都在同一個工作目錄裡修改,.evidence.json 還是可能記到其他工作的檔案,測試與 commit 時也可能一起受到影響。

我又很常在前一份 change 還沒 archive 時,就開始下一份;有時還會同時開幾個 sessions,讓不同 Agent 分別處理。這時候,每個 Agent 看到的卻是同一份工作目錄,裡面也包含別份 change 尚未完成的修改。

【Day - 12】介紹過,Worktree 可以替不同的工作分開目錄,我在 Speclink 也沿用了這個方向。不過,目錄分開以前,還得先確認哪些 changes 可以一起做;做完以後,也要把修改檢查好,再合回主要的 branch。

什麼時候需要把工作目錄分開?

Git Worktree 可以讓同一個 repo 同時有多個工作目錄。在 Speclink 裡,每份 change 可以使用自己的目錄與 branch,讓不同 Agent 分別修改,不會直接動到另一邊還在處理的檔案。

不過,我也不是每次都需要 Worktree。有時候只處理一份 change,直接在主要工作目錄做完就好了;準備同時進行好幾份 changes,或想把某份工作的修改分開時,再使用 Worktree。

但目錄分開,就能一起開始做了嗎?假設 MFA 要沿用另一份 change 準備的登入提示,登入提示還沒完成,MFA 就得先等一下。替兩邊各開一個 Worktree,也不會改變這個先後關係。所以,在安排不同 Agent 開工以前,我還需要先確認:哪些 changes 可以一起做,哪些得等前面的工作完成?

多份 changes,哪些可以先開工?

一開始,我是在 propose 的收尾加上順序提醒。當時如果還有好幾份 changes 沒開始,AI 就會看看它們的規劃,告訴我哪些可以一起做、哪些最好先做完再接下一份。

但實際用起來,只有這個提醒還是不太夠。我最常遇到的情況是:看完建議,先去做 Change A,過一段時間回來,就忘了 A 後面應該接哪一份。這時候又得回頭找前面的對話,或再問 AI 一次:「哪些 changes 要先做?哪些需要等其他工作完成?」

因為這些建議只留在當下的對話裡,換個 session,或回到看板,都看不到之前排好的順序。所以後來,我讓 Speclink 把 changes 之間的先後關係記下來。

為了方便說明,我請 Codex 建立了三份示範用的 changes,分別是:

Change 要做什麼? 需要先等誰完成?
improve-login-feedback 改善登入失敗的提示。 不需要等待其他 change。
improve-report-export 改善報表匯出的提示。 不需要等待其他 change。
add-mfa 加入第二步驗證,沿用登入提示的結果。 等 improve-login-feedback 完成並 archive。

像 MFA 需要等登入提示完成,這個關係就得留下來。propose 時,AI 會判斷這些先後關係,再透過 change depends 記下來。【Day - 17】看過 .openspec.yaml 如何保存討論來源,這裡則多了 depends_on,用來記錄需要先完成的 changes。像 MFA 的這份檔案,就會包含:

# openspec/changes/add-mfa/.openspec.yaml
# 僅列出這次新增的欄位
depends_on: improve-login-feedback

之後換個 session,也能從這份檔案找到需要等待的 change。至於兩份 changes 是否都要修改同一個 capability 的 delta 規格,則由引擎直接比對,不需要 AI 另外寫進 depends_on。

如果後來透過 ingest 修改需求,AI 也會確認這份 change 是否開始依賴其他工作的成果,並把新的依賴記下來。原本已經記下的依賴不會自動刪除;如果不需要再等了,則由我們確認後移除。

把先後關係記下來後,我就不用每次都翻對話、重新問 AI。接下來只要查看目前有哪些 changes 可以開始,哪些還需要等待就好了。Speclink 用來整理這份順序的指令,就是 speclink plan。

它會根據剛才記下的關係,以及規格是否重疊,把 changes 分成幾批,AI 稱它為「波次」。第一波是目前可以開工的 changes,需要等待的則排到後面。同一波表示依目前的紀錄可以分別交給不同 Agent,但 speclink plan 只會列出安排,實際要開幾個 sessions、什麼時候開始,仍然由我們決定。

把剛才的三份 changes 排進波次,就會像下面這樣:

第一波的登入提示與報表匯出可以分開進行;MFA 需要沿用登入提示的結果,也會修改 auth 規格,因此排在第二波,等待登入提示完成並 archive。波次不會自動啟動 Agent sessions

同樣的安排也會顯示在 Speclink Desktop 的看板上。卡片上的圓圈數字代表波次,需要等待的卡片會變淡;滑過數字,就能看到它正在等哪份 change:

Speclink Desktop 的 login-demo 看板:報表匯出與 Worktree 中完成 2/4 tasks 的登入提示同屬第一波,等待登入提示的 add-mfa 顯示第二波並整卡變淡

除了在看板上查看,開始 apply 時,AI 也會依照 Skill 指引執行 speclink plan --json,確認這次要做的 change 是否可以開工。沒有指定 change,就選下一個尚未開工、也不需要等待的項目;指定的 change 還在等其他工作完成,就先說明原因並停下來。以前面的範例來說,報表匯出可以開始,MFA 則要繼續等登入提示收尾。

確定可以開工後,再選擇工作目錄

有了前面的波次安排,我就知道哪些 changes 可以同時進行。不過,原本沿用 Spectra 的 apply,會直接在目前的工作目錄裡處理 tasks。如果開了幾個 Agent sessions,卻都在同一個目錄裡修改,還是會遇到前面說的問題:不同 changes 的修改混在一起。

所以,我在 Speclink 另外加入了 apply-with-worktree Skill,先替這份 change 準備獨立的工作目錄,再讓 AI 在裡面繼續實作。只做一份 change、不需要分開目錄時,就使用原本的 apply;需要讓不同 changes 分開進行時,再使用 apply-with-worktree:

Speclink 準備進入 apply 時,可以依照是否需要獨立工作目錄選擇兩條路:想留在主要工作目錄時使用 apply;需要隔離一份 change,或讓多份 changes 平行實作時,則使用 apply-with-worktree

如果決定使用 Worktree,就要先在 Speclink 開啟這項功能。設定會保存在【Day - 8】介紹過的 openspec/config.yaml 裡。透過 Speclink 的設定指令開啟時,CLI 會更新設定,並替專案已設定的工具產生 apply-with-worktree 與 worktree-merge 兩個 Skills。

之後可以關閉嗎? 可以,關閉時會移除這兩個 Skills,一般的 apply 仍然保留。不過,如果還有正在處理 change 的 Worktree,就得先完成合併,才能關閉。

開始 apply-with-worktree 時,也會先在主要工作目錄執行 speclink plan,確認這份 change 存在、尚未 archive,而且可以開工。如果還需要等待,就先停下來,不會先提交文件或建立 Worktree。即使是回到舊 Worktree 繼續做,也以主要工作目錄的結果為準,避免裡面還留著前置 change 尚未歸檔的舊資料。

確認可以開始後,接著才要處理:這份 change 的規劃文件,能不能一起帶進新的工作目錄?

規劃完成後,怎麼在 Worktree 開始實作?

一般情況下,propose 先準備好 proposal、specs、design 與 tasks,接著才建立 Worktree,讓 AI 在裡面開始實作。不過,新的 Worktree 是從 Git commit 建立的;如果規劃文件還沒提交,新的目錄裡就不會有這份 change。

因此,Skill 會先確認規劃文件已經提交。如果還沒提交,就只提交這份 change 的目錄,再從目前的 commit 建立新的 branch 與 Worktree。AI 進去後,就能讀取這些文件,照著 tasks 開始做。

這和【Day - 12】介紹的 Spectra 作法稍微不同。當時 Spectra Desktop 會把 change artifacts 搬進 Worktree;Speclink 則透過 Git commit 把文件帶進新目錄,主要工作目錄也會保留原本的 change。

如果已經做到一半呢? 若先在主要工作目錄實作過,Skill 會依 .evidence.json 的檔案紀錄,檢查是否還有未提交的修改。有的話就先列出來,讓我選擇先提交這份 change 的 code、確認這些修改不會帶過去後仍然繼續,或停止。這是為了避免新 Worktree 裡的 tasks 已經打勾,對應的 code 卻還留在原本的目錄。沒有 evidence 或檔案清單是空的,就會略過這項檢查。

如果這份 change 已經有 Worktree,則會沿用原本的 branch 與目錄繼續做,不會重新建立。這和新建不同:主要工作目錄後來新增的 commit,不會因為再次執行 apply-with-worktree 就自動同步進舊 Worktree。

把一般流程與需要額外確認的情況放在一起,就會像下面這樣:

apply-with-worktree 先確認可以開工與規劃文件已提交,再準備 Worktree 開始實作;若 evidence 有檔案紀錄,會先檢查其中的未提交修改,必要時由使用者決定。新建從目前 commit 建立,沿用舊 Worktree 則不會自動同步主目錄的新提交

一份 Worktree 也有自己的成本: 原始碼雖然會出現在新的工作目錄,相依套件與建置產物卻不會一起複製。第一次進去執行測試或 build,通常還是得重新安裝與建置。

每份 change 各開一個 session,進度怎麼看?

Worktree 建好後,apply 要做的事情沒有改變:讀取 artifacts、逐項完成 tasks,並在 task done 找到尚未記錄的檔案異動時留下 evidence。差別只在於,讀取檔案、修改 code、執行測試,甚至呼叫 command,都要在這一份 change 的工作目錄裡進行。

Speclink 也特別限制,一個 apply-with-worktree session 只處理一份 change。以前面的範例來說,登入提示已經在一個 Worktree 裡做到一半;如果想同時進行報表匯出,就另外開一個 session,替它建立自己的 Worktree。

兩個 sessions 就能各自在自己的 Worktree 裡持續處理工作。如果只用一個 session 逐一切換,仍然是在做完一邊後再處理另一邊,也會把不同需求留在同一段對話裡。

幾份 changes 分開實作後,我也不需要逐一打開每個 Worktree 才知道進度。回到主要工作目錄,speclink list 與 Speclink Desktop 都可以顯示它們各自位於哪一個 Worktree、使用哪一條 branch,以及完成了多少 tasks。

進度是從哪裡來的? Speclink 直接讀取 Git 的 Worktree 清單,再用 speclink/<change-name> branch 與裡面的同名 change 目錄找到對應進度。沒有對應 Worktree 時,就讀取主要工作目錄裡的紀錄;Worktree 被移除後,畫面上的標記也會跟著消失,不需要另外維護一份對照表。

下面是加入波次顯示以前的畫面,這裡先看卡片上的 2/4 tasks 進度,以及滑過 Worktree 圖示後顯示的 branch:

寫這篇鐵人賽文章時的 Speclink Desktop,improve-login-feedback change 在 Worktree 中進行,卡片顯示 branch 與 2/4 tasks 的進度

tasks 做完後,先不要急著合併

Worktree 裡的 tasks 全部完成後,apply-with-worktree 會先替這份 change 提交 code、artifacts 與 evidence,接著停在原地。它不會順手合併回主要 branch,也不會立刻移除 Worktree。

我通常會先留在同一個 Worktree 裡,依照這次 change 的風險,檢查 code 與規格是否一致,以及修改有沒有帶來其他問題。這時候,整份 change 的修改都在同一個目錄裡,比較容易集中確認。

檢查完成後,如果又修改了 code 或更新了 change 文件,也要一起補進 commit。我另外準備了 worktree-merge Skill,負責接著確認合併條件,再把這份 change 合回主要 branch。

從進入 apply 到最後 archive,順序就會變成下面這樣:

一份 change 先經過 propose,再於 Worktree 完成 apply、tasks、evidence、commit 與需要的品質檢查,提交檢查結果後透過 worktree-merge 合回主要工作目錄,最後才執行 archive

準備合併時,主要 branch 可能也已經有其他修改。worktree-merge 會怎麼處理這些變動,遇到衝突又會停在哪裡呢?

worktree-merge 怎麼合併,遇到衝突又怎麼處理?

執行 worktree-merge 時,Skill 會先從主要工作目錄確認這次要合回哪一條 branch,以及兩邊的修改是不是都已經提交。如果主要工作目錄還有未提交的修改,或 Worktree 裡還有內容沒提交,就先停下來,讓我處理好再繼續。

確認好後,它會先讓 Worktree 裡的這份 change 接上主要 branch 的最新進度,再把修改合回去。如果接上最新進度時遇到衝突,就會取消這次嘗試,改用一般的合併方式;如果準備合回去時,主要 branch 又有了新的提交,也會改用一般合併。

如果一般合併仍然遇到衝突,Skill 就會取消合併、列出衝突檔案,停下來讓我決定怎麼處理。圖中的 rebase、fast-forward 與 merge,就是這幾個步驟使用的 Git 操作:

Speclink 的 worktree-merge 先嘗試 rebase 與 fast-forward;rebase 衝突或 fast-forward 被拒時改用一般 merge,一般 merge 仍衝突則中止並交回使用者決定

我實際使用時,也常會在這裡請 AI 協助分析與修正衝突。不過,要怎麼改,還是得另外交代。尤其兩份 changes 都改到同一段功能時,需要確認的可能是「最後要保留哪一種行為」,光是把 Git 標出的衝突消掉,還不代表功能就做對了。

只有合併成功後,worktree-merge 才會移除這份 change 的 Worktree 與 branch。接著,就能在主要工作目錄執行 archive,把這次的規格變更合併進正式 specs。Speclink 不允許直接在 Worktree 裡 archive,確保這些修改已經合回主要 branch,再完成歸檔。

工作目錄分開了,還有哪些事情要確認?

有了前面的安排,我就能先確認哪些 changes 可以一起做,再讓不同的 Agent sessions 各自在自己的 Worktree 裡實作。每份 change 的程式修改、測試與完成紀錄都留在各自的目錄裡,比較不會混在一起。

但工作目錄分開了,還是有些事情需要一起確認。speclink plan 會根據已記下的先後關係與規格重疊安排順序,卻無法事先知道兩份 changes 實作時會不會改到同一段 code。另外,共用的資料庫、測試帳號或遠端服務,也不會因為建立了 Worktree 就各自多出一份。

就算這些地方都確認過,幾份 changes 可以互不干擾地進行,也不代表每一份都已經做對。功能有沒有照規格完成、code 是否容易維護,還是要各自檢查。所以,我會先在 Worktree 裡確認這份 change 的修改,再把它合回主要 branch。

那麼,這些檢查可以怎麼做呢?接下來,就來看看 Speclink 的 Review 與 Verify 分別負責什麼吧!

參考資料


上一篇
【Day - 20】Speclink 怎麼分配 tasks,又怎麼留下完成紀錄?
下一篇
【Day - 22】Tasks 都打勾了,Review 與 Verify 還要檢查什麼?
系列文
我的 SDD 實驗之路 - 從實際使用現有工具,到設計自己的流程 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言